//
// Created by martin on 2/8/22.
//

#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS // use C99 micro definition for C++
#endif

#include "TimerQueue.h"

#include "muduo/base/Logging.h"
#include "muduo/net/Timer.h"
#include "muduo/net/TimerId.h"
#include "muduo/net/Channel.h"
#include "muduo/net/EventLoop.h"

#include <sys/timerfd.h>
#include <unistd.h>
#include <iterator>

namespace muduo
{
namespace net
{
namespace detail
{

int createTimerfd()
{
    // create timers that notify via fd
    int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
    if (timerfd < 0)
    {
        LOG_SYSFATAL << "Failed in timerfd_create";
    }
    return timerfd;
}

/**
 * 计算when到now的时间差, 以timespec结构表示
 */
struct timespec howMuchTimeFromNow(Timestamp when)
{
    int64_t microseconds = when.microSecondsSinceEpoch() - Timestamp::now().microSecondsSinceEpoch();
    if (microseconds < 100)
    {
        microseconds = 100;
    }
    struct timespec ts;
    ts.tv_sec = static_cast<time_t>(microseconds / Timestamp::kMicroSecondsPerSecond);
    ts.tv_nsec = static_cast<long>((microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);
    return ts;
}

/**
 * 重新设置timerfd超时时间
 * @param timerfd 指定的timerfd, 由timerfd_create(2)创建
 * @param expiration 超时时间点
 */
void resetTimerfd(int timerfd, Timestamp expiration)
{
    // wake up loop by timerfd_settime()
    struct itimerspec newValue;
    struct itimerspec oldValue;
    memZero(&newValue, sizeof(newValue));
    memZero(&oldValue, sizeof(oldValue));
    newValue.it_value = howMuchTimeFromNow(expiration);
    int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue); // relative timer
    if (ret)
    {
        LOG_SYSERR << "timerfd_settime()";
    }
}

/**
 * 读取超时次数, 便于debug/trace
 */
void readTimerfd(int timerfd, Timestamp now)
{
    // timer超时后, 读取超时的次数
    uint64_t howmany;
    ssize_t n = ::read(timerfd, &howmany, sizeof(howmany));
    LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString();
    // successful read return an uint64_t value
    if (n != sizeof(howmany))
    {
        LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8";
    }
}

} // namespace detail
} // namespace net
} // namespace muduo

using namespace muduo;
using namespace muduo::net;
using namespace muduo::net::detail;

TimerQueue::TimerQueue(EventLoop *loop)
: loop_(loop),
timerfd_(createTimerfd()),
timerfdChannel_(loop, timerfd_),
timers_(),
callingExpiredTimers_(false)
{
timerfdChannel_.setReadCallback(std::bind(&TimerQueue::handleRead, this));
// we are always reading the timerfd, we disrm it with timerfd_settime.
timerfdChannel_.enableReading();
}

TimerQueue::~TimerQueue()
{
    // 关闭所有(通道)事件, Poller不再监听该通道
    timerfdChannel_.disableAll();
    // 如果正在处理该通道, 会从激活的通道列表中移除, 同时Poller不再监听该通道
    timerfdChannel_.remove();
    // 关闭通道对应timerfd
    ::close(timerfd_);

    // FIXME: I dont understand why "do not remove channel". What does it mean?
    // do not remove channel, since we're in EventLoop::dtor();

    // TimerQueue::addTimer中new出来的Timer对象, 需要手动delete
    for (const Entry& timer : timers_)
    {
        delete timer.second;
    }
}

/**
 * 添加一个定时器.
 * @details 运行到指定时间点when, 调度相应的回调函数cb.
 * 如果interval参数 > 0.0, 就周期重复运行.
 * 可能会由其他线程调用, 需要让对TimerQueue数据成员有修改的部分, 在所属loop所在线程中运行.
 * @param cb 超时回调函数
 * @param when 触发超时的时间点
 * @param interval 循环周期. > 0.0 代表周期定时器; 否则, 代表一次性定时器
 * @return 返回添加的Timer对应TimerId, 用来标识该Timer对象
 */
TimerId TimerQueue::addTimer(TimerCallback cb, Timestamp when, double interval)
{
    Timer* timer = new Timer(std::move(cb), when, interval);
    loop_->runInLoop(std::bind(&TimerQueue::addTimerInLoop, this, timer));
    return TimerId(timer, timer->sequence());
}

/**
 * 取消一个定时器, 函数可能被别的线程调用
 * @param timerId 待取消Timer唯一标识
 */
void TimerQueue::cancel(TimerId timerId)
{
    loop_->runInLoop(
            std::bind(&TimerQueue::cancelInLoop, this, timerId));
}

/**
 * 在loop线程中添加一个定时器.
 * @details addTimerInLoop 必须在所属loop线程中运行
 */
void TimerQueue::addTimerInLoop(Timer *timer)
{
    loop_->assertInLoopThread();
    bool earliestChanged = insert(timer);

    /* timers_为空, 或者 timer已经超时, 需要重置定时器 */
    if (earliestChanged)
    {
        resetTimerfd(timerfd_, timer->expiration());
    }
}

/**
 * 在所属loop线程中, 取消一个定时器
 * @details 同时擦出timers_, activeTimers_中包含的Timer对象, timerId用来查找该Timer对象.
 * @param timerId 待取消Timer的唯一Id标识
 */
void TimerQueue::cancelInLoop(TimerId timerId)
{
    loop_->assertInLoopThread(); // 确保当前线程是所属loop线程
    assert(timers_.size() == activeTimers_.size());
    ActiveTimer timer(timerId.timer_, timerId.sequence_);
    ActiveTimerSet::const_iterator it = activeTimers_.find(timer);
    if (it != activeTimers_.end())
    {
        // 注意timers_和activeTimers_的Timer指针指向相同对象, 只能delete一次
        size_t n = timers_.erase(Entry(it->first->expiration(), it->first));
        assert(n == 1); (void)n;
        delete it->first; // FIXME: no delete please
        activeTimers_.erase(it);
    }
    else if (callingExpiredTimers_)
    { // 如果正在处理超时定时器
        cancelingTimers_.insert(timer);
    }
    assert(timers_.size() == activeTimers_.size());
}

/**
 * 处理读事件, 只能是所属loop线程调用
 * @details 当PollPoller监听到超时发生时, 将channel加入激活通道列表, loop中回调
 * 事件处理函数, TimerQueue::handleRead.
 * 发生超时事件时, 可能会有多个超时任务超时, 需要通过getExpired一次性全部获取, 然后逐个执行回调.
 * @note timerfd只会发生读事件.
 */
void TimerQueue::handleRead()
{
    loop_->assertInLoopThread();
    Timestamp now(Timestamp::now());
    readTimerfd(timerfd_, now);

    std::vector<Entry> expired = getExpired(now); // 获取所有超时任务

    // 正在调用超时任务回调时, 先清除取消的超时任务cancelingTimers_, 再逐个执行超时回调.
    // 可由getExpired()获取的所有超时任务.
    callingExpiredTimers_ = true;
    cancelingTimers_.clear();
    // safe to callback outside critical section
    for (const Entry& it : expired)
    {
        it.second->run();
    }
    callingExpiredTimers_ = false;
    // 重置所有已超时任务
    reset(expired, now);
}

/**
 * 插入一个timer指向的定时器
 * @details timers_是std::set<std::pair<Timestamp, Timer*>>类型, 容器会自动对元素进行排序,
 * 默认先按pair.first即Timestamp进行排序, 其次是pair.second(.first相同情况下才比较second),
 * 这样第一个元素就是时间戳最小的元素.
 * @return 定时器timer当前是否已经超时
 * - true timers_为空或已经超时
 * - false timers_非空, 且最近的一个定时器尚未超时
 */
bool TimerQueue::insert(Timer *timer)
{
    loop_->assertInLoopThread();
    assert(timers_.size() == activeTimers_.size());
    bool earliestChanged = false;
    Timestamp when = timer->expiration(); // 超时时刻
    TimerList::iterator it = timers_.begin();
    if (it == timers_.end() || when < it->first)
    { // 定时器集合为空 或者 新添加的timer已经超时(因为it指向的Timer超时时刻是距离当前最近的)
        earliestChanged = true; // timer已经超时
    }

    // 同时往timers_和activeTimers_集合中, 添加timer
    // 注意: timers_和activeTimers_元素类型不同, 但所包含的Timer是相同的, 个数也相同

    { // ensure insert new timer to timers_ successfully
        std::pair<TimerList::iterator, bool> result
        = timers_.insert(Entry(when, timer));
        assert(result.second); (void)result;
    }

    { // ensure insert new timer to activeTimers_ successfully
        std::pair<ActiveTimerSet::iterator, bool> result
        = activeTimers_.insert(ActiveTimer(timer, timer->sequence()));
        assert(result.second); (void)result;
    }

    assert(timers_.size() == activeTimers_.size());
    return earliestChanged;
}

/**
* 定时任务超时时, 从set timers_中取出所有的超时任务, 以vector形式返回给调用者
* @note 注意从set timers_要和从set activeTimers_同步取出超时任务, 两者保留的定时任务是相同的
* @param now 当前时间点, 用来判断从set中的定时器是否超时
* @return set timers_中超时的定时器
*/
std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now)
{
    assert(timers_.size() == activeTimers_.size());
    std::vector<Entry> expired;
    Entry sentry(now, reinterpret_cast<Timer*>(UINTPTR_MAX));
    // end.key >= sentry.key, Entry.key is pair<Timestamp, Timer*>
    // in that end.key.second < sentry.key.second(MAX PTR)
    // => end.key == sentry.key is impossible
    // => end.key > sentry.key
    TimerList::iterator end = timers_.lower_bound(sentry);
    assert(end == timers_.end() || now < end->first);
    std::copy(timers_.begin(), end, back_inserter(expired));
    timers_.erase(timers_.begin(), end);

    for (const Entry& it : expired)
    {
        ActiveTimer timer(it.second, it.second->sequence());
        size_t n = activeTimers_.erase(timer);
        assert(n == 1); (void)n;
    }

    assert(timers_.size() == activeTimers_.size());
    return expired;
}

/**
 * 根据指定时间now重置所有超时任务, 只对周期定时任务有效
 * @param expired 所有超时任务
 * @param now 指定的reset基准时间点, 新的超时时间点以此为基准
 */
void TimerQueue::reset(const std::vector<Entry> &expired, Timestamp now)
{
    Timestamp nextExpire;

    for (const Entry& it : expired)
    {
        ActiveTimer timer(it.second, it.second->sequence());
        // 只重置周期定时任务和没有取消的定时任务, 释放一次性超时的定时任务
        if (it.second->repeat()
        && cancelingTimers_.find(timer) == cancelingTimers_.end())
        {
            it.second->restart(now);
            insert(it.second);
        }
        else
        {
            // FIXME move to a free list
            delete it.second; // FIXME: no delete please
        }
    }

    // 根据最近的尚未达到的超时任务, 重置timerfd下一次超时时间
    if (!timers_.empty())
    {
        nextExpire = timers_.begin()->second->expiration();
    }

    if (nextExpire.valid())
    {
        resetTimerfd(timerfd_, nextExpire);
    }
}


